package com.zh.webcommon.common.log;

import com.zh.webcommon.common.auth.LoginUserHolder;
import com.zh.webcommon.common.log.annotation.OperateLog;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.InputStreamSource;
import org.springframework.http.HttpEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;

import java.io.Closeable;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static com.zh.webcommon.CommonConst.GSON;
import static com.zh.webcommon.common.log.OperationLogRecorder.NO_USER;

/**
 * 操作记录打印
 *
 * @author ZH
 * @date 16:03 2020/6/22
 */
@Slf4j
@Order(2)
@Aspect
public class OperatingLogAspect {
    public OperatingLogAspect() {
        log.info("[OperatingLogAspect] constructor ...");
    }

    private OperationLogRecorder operationLogRecorder;

    @Autowired(required = false)
    public void setOperationLogRecorder(OperationLogRecorder operationLogRecorder) {
        this.operationLogRecorder = operationLogRecorder;
    }

    @Pointcut("@within(org.springframework.web.bind.annotation.RestController) && @annotation(operateLog)")
    public void optLogPointcut(OperateLog operateLog) {
    }

    /**
     * 前置通知
     * 记录用户的访问操作
     */
    @Before("optLogPointcut(operateLog)")
    public void doBefore(JoinPoint joinPoint, OperateLog operateLog) {
        String remoteHost = getRemoteHost();
        String uri = getRequestURI();

        String operation = operateLog.value();

        String reqParams = getRequestParamsStr(joinPoint);

        var username = LoginUserHolder.currentUsername();
        if (operationLogRecorder!=null){
            operationLogRecorder.accessLog(operation, uri, reqParams, remoteHost, StringUtils.isEmpty(username) ? NO_USER : username);
        }
    }

    /**
     * 获取请求数据，转为json字符串
     *
     * @param joinPoint
     * @return
     */
    private String getRequestParamsStr(JoinPoint joinPoint) {
        List<Object> params = getRequestParams(joinPoint);
        try {
            return GSON.toJson(params);
        } catch (Exception ex) {
            log.warn("格式化Json出错. cause = {}", ex.getMessage());
            return "";
        }
    }

    /**
     * 获取请求数据，转为json字符串
     *
     * @param joinPoint
     * @return
     */
    private List<Object> getRequestParams(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();

        return Arrays.stream(args).map(e -> {
            if (e instanceof ServletRequest) {
                return ((ServletRequest) e).getParameterMap();
            }
            return e;
        }).filter(e -> !notJson(e)).toList();
    }

    /**
     * 获取请求来自客户端host
     *
     * @return
     */
    private String getRemoteHost() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest().getRemoteHost();
    }

    /**
     * 获取请求的URI
     *
     * @return
     */
    private String getRequestURI() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest().getRequestURI();
    }

    private boolean notJson(Object obj) {
        if (obj.getClass().isArray() || obj instanceof List) {
            List<?> objects = obj.getClass().isArray() ? CollectionUtils.arrayToList(obj) : (List<?>) obj;
            if (objects.isEmpty()) {
                return false;
            } else {
                return notJson(objects.get(0));
            }
        }
        return (obj instanceof ServletRequest || obj instanceof ServletResponse || obj instanceof HttpEntity || obj instanceof InputStreamSource || obj instanceof File || obj instanceof Closeable || obj instanceof ResponseBodyEmitter);
    }

}